#include "texturecontainer.h"

CTextureContainer* CTextureContainer::getInstance() 
{
    /**
     * @return CTextureContainer* A pointer to the static singleton
     */
    static CTextureContainer instance;
    return &instance;
}

CTextureContainer::~CTextureContainer()
{
    /**
     * On destruction, ensure that all the textures are returned to the
     * heap to prevent memory leaks.
     */
    if (m_arrTextures.size() > 0)
    {
        for (map<string,CTexture*>::iterator i = m_arrTextures.begin(); 
             i != m_arrTextures.end(); ++i)
        {
            delete i->second;
        }
        m_arrTextures.clear();
    }
}

CTexture* CTextureContainer::addTexture(const string &key, const string &path, 
                                        LPDIRECT3DDEVICE9 device, int nWidth,
                                        int nHeight) 
{
    /**
     * Adds a texture to the container. Indexed by the key passed in. If the key
     * already exists, then that current texture will be removed and the
     * memory reallocated to the heap and replaced by the new texture.
     *
     * @param const string&key The key to index the texture
     * @param const string &path The path to the texture of where the file is
     *                           loaded
     * @param LPDIRECT3DDEVICE9 device The directx device on which to load the
     *                                 texture.
     * @param int nWidth The width of the texture
     * @param int nHeight THe height of the texture
     */
    map<string, CTexture*>::const_iterator pTexturePos(m_arrTextures.find(key));
    if (pTexturePos != m_arrTextures.end())
    {
        // Key already exist, remove.
        return pTexturePos->second;
    }
    CTexture* pTexture = new CTexture(path, device);
    if (pTexture)
    {
        m_arrTextures.insert(pair<string,CTexture*>(key, pTexture));
        return pTexture;
    }
    stringstream sstream;
    sstream << "Could not allocate enough memory to create the texture: "
            << path;
    MessageBox(NULL, sstream.str().c_str(), 
        "CTextureContainer::addMesh", MB_OK);
    return nullptr;
}

CTexture* CTextureContainer::addTexture(const string &key, 
                                        LPDIRECT3DTEXTURE9 texture, int nWidth,
                                        int nHeight) 
{
    /**
     * Adds a texture from a given texture object. Since we have no idea where
     * this texture object has come from (i.e has it already been loaded?) we
     * cannot use another texture reference. This means we'll have to create
     * a new texture object and copy it in.
     *
     * @param const string &key The key to index the texture
     * @param LPDIRECT3DTEXTURE9 texture The texture to load.
     * @param int nWidth The width of the texture
     * @param int nHeight THe height of the texture
     */
    map<string, CTexture*>::const_iterator pTexturePos(m_arrTextures.find(key));
    if (pTexturePos != m_arrTextures.end())
    {
        // Key exists, overwrite
        delete pTexturePos->second;
        m_arrTextures.erase(pTexturePos);
    }
    if (CTexture* pTexture = new CTexture(texture))
    {
        m_arrTextures.insert(pair<string,CTexture*>(key,pTexture));
        return pTexture;
    }
    stringstream sstream;
    sstream << "Could not allocate enough memory to create the texture: "
            << key;
    MessageBox(NULL, sstream.str().c_str(), 
        "CTextureContainer::addMesh", MB_OK);
    return nullptr;
}

bool CTextureContainer::removeTexture(const string &key) 
{
    /**
     * Removes a texture. This decrements the reference count to the texture
     * object. If the reference count of the object falls to 0 or below, it
     * means we have no more use for the texture and it can be destroyed.
     *
     * @param const string &key The key to the texture to remove.
     */
    map<string,CTexture*>::iterator i = m_arrTextures.find(key);
    if (i == m_arrTextures.end())
    {
        return false;
    }

    delete i->second;
    m_arrTextures.erase(i);

    return true;
}

CTexture* CTextureContainer::getTexture(const string &key) const 
{
    /**
     * @param const string &key The key of the texture
     * @return CTexture* The pointer to a texture object index by key.
     */
    map<string,CTexture*>::const_iterator i(m_arrTextures.find(key));
    if (i == m_arrTextures.end())
    {
        // Texture key does not exist.
        return (nullptr);
    }
    return(i->second);
}

void CTextureContainer::clearTextures()
{
    /**
     * Clears all textures from memory and resets the container. This should
     * be called at map unload to unload all the textures.
     */
    if (m_arrTextures.size() > 0)
    {
        for (map<string, CTexture*>::iterator i = m_arrTextures.begin(); 
             i != m_arrTextures.end();)
        {
            if (! i->second->isPermanent())
            {
                delete i->second;
                i = m_arrTextures.erase(i);
            }
            else
            {
                ++i;
            }
        }
    }
}